#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2017/3/21 9:59
# @Author  : 柳晓飞
# @Site    : 
# @File    : logic.py
# @Software: PyCharm
import deal
from enum import Enum, unique
import card
import handle.gameServerHand as gmServerHand
import net.protocal as protocal
import jjGlobal.jjGlobal as jjGlobal
import yaml
import json

@unique
class LogicStatus(Enum):#当前状态
    Busy = 0
    Idle = 1

class Logic:
    def __init__(self):
        self.table = None
        self.status = LogicStatus.Idle.value
        self.curPlayer = None
        self.curNotifyPlayer = None#当前通知可碰、杠、听、胡的玩家，如果玩家点击了碰、杠、听、胡则将该值赋值给curPlayer
        self.curNotifyOpType = 0#当前通知玩家可碰、杠、听、胡的类型
        self.callFuc = None
        self.leftTime = 0# 单位秒
        self.bankerID = 0# 庄家ID
        self.curOutCardNum = 0#当前出的牌值
        self.mqRate = 2
        self.mgRate = 2
        self.agRate = 2
        self.zmRate = 2
    def customInit(self):
        jjGlobal.gameserver.gameServerHand.registMsg(protocal.operate,self, self.reqControlCard)

    def start(self):
        if(self.status == LogicStatus.Busy):
            return
        if self.table.size != None and len(self.table.posDict) >= self.table.size:
            self.status = LogicStatus.Busy.value
            self.addDelayCall(2,self.dealCards)#    2秒后开始发牌

    def dealCards(self):
        print '发牌协议'
        playerNum = len(self.table.posDict)
        playersCardsDict, self.table.leftCardList = deal.deal(playerNum)
        for p in self.table.posDict.values():
            self.bankerID = p.playerID
            self.curPlayer = p
            break
        print 'bankerID = %d'%self.bankerID

        cardsIndex= 0
        for p in self.table.posDict.values():
            p.reSetPStatus()
            p.cardList = playersCardsDict[cardsIndex]
            p.isPlaying = True
            msg = {}
            msg['id'] = protocal.deal
            msg['cardList'] = p.cardList
            d = json.dumps(msg)
            jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID,d)
            cardsIndex += 1

        self.addDelayCall(2,self.opSendCard)#1秒后给庄家一张牌

    def end(self,winerP):#
        print '游戏结束'
        self.resNotifyResult()
        #清除掉线玩家
        self.clearOfflinePlayer()
        self.addDelayCall(5, self.reStart)
    def reStart(self):
        self.status == LogicStatus.Idle
        self.start()

    def update(self):#每秒调用一次，事件驱动 即通过countTime websocket网络来驱动
        if self.leftTime > 0:
            self.leftTime -= 1
            if self.leftTime == 0:
                self.callFuc()
    def addDelayCall(self,sec,fuc):
        self.leftTime = sec
        self.callFuc = fuc
    def cancalDelayCall(self):
        self.leftTime = 0
        self.callFuc = None

    #玩家操作超时
    def opTimeOut(self):
        print '操作牌超时'
        if self.curPlayer.playerID == self.curNotifyPlayer.playerID:#出牌超时
            cardNum = self.curPlayer.cardList.pop()
            self.resOpResult(self.curPlayer, card.OpType.Chu.value, cardNum)
            self.opChuRes(cardNum)
        else:#操作牌超时
            self.recursiveOutCardNum(self.getNextPlayer(self.curNotifyPlayer.playerID),self.curNotifyOpType,self.curOutCardNum)
    #给玩家发一张牌
    def opSendCard(self):
        print ('给玩家发一张牌')
        print ('playerID = %d'%self.curPlayer.playerID)
        if len(self.table.leftCardList) <= 0:
            print '没有牌了'
            self.end(None)
            return
        cardNum = self.table.leftCardList.pop()
        if self.isHu(self.curPlayer.cardList, cardNum):#自摸
            self.resNotifyOpCard(self.curPlayer,card.OpType.Hu.value,cardNum)
        elif self.isGang(self.curPlayer.lockList, cardNum):#明杠
            self.resNotifyOpCard(self.curPlayer, card.OpType.Gang.value, cardNum)
        elif self.isGang(self.curPlayer.cardList, cardNum):#暗杠
            self.resNotifyOpCard(self.curPlayer, card.OpType.Gang.value, cardNum)

        self.curPlayer.cardList.append(cardNum)
        self.resNotifyOpCard(self.curPlayer,card.OpType.Get.value,cardNum)
        self.notifyUpdateData()
        self.addDelayCall(16,self.opTimeOut)
    def notifyUpdateData(self):
        for p in self.table.posDict.values():
            msg = {}
            msg['id'] = protocal.notifyUpdateData
            msg['leftCardNum'] = len(self.table.leftCardList)
            d = json.dumps(msg)
            jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID, d)
    #前端出牌请求
    def opChuReq(self,data):
        cardNum = data['cardNum']
        if cardNum in self.curPlayer.cardList:
            self.curPlayer.cardList.remove(cardNum)
            self.resOpResult(self.curPlayer, card.OpType.Chu.value, cardNum)
            self.opChuRes(cardNum)
        else:
            print '玩家出了一张未知的牌'
            print cardNum
    #处理玩家出牌后的逻辑
    def opChuRes(self,outCardNum):
        #通知出牌结果
        self.resNotifyOpRes(card.OpType.Chu.value,outCardNum)
        self.curOutCardNum = outCardNum
        #判断是否有胡、杠、碰的玩家
        notifyP = self.getNextPlayer(self.curPlayer.playerID)
        self.recursiveOutCardNum(notifyP,card.OpType.Hu.value,outCardNum)

    #出一张牌后判断其他人是否需要此牌,递归判断是否胡、杠、碰
    def recursiveOutCardNum(self,notifyP,opType,cardNum):
        if notifyP.playerID == self.curPlayer.playerID:
            if opType == card.OpType.Hu.value:
                opType = card.OpType.Gang.value
                notifyP = self.getNextPlayer(notifyP.playerID)
            elif opType == card.OpType.Gang.value:
                opType = card.OpType.Peng.value
                notifyP = self.getNextPlayer(notifyP.playerID)
            elif opType == card.OpType.Peng.value:
                p = self.getNextPlayer(self.curPlayer.playerID)
                self.curPlayer = p;
                self.opSendCard()
                return
            else:
                print 'recursiveOutCardNum 失败'


        self.curNotifyOpType = opType
        cardList = notifyP.cardList
        lockList = notifyP.lockList
        if opType == card.OpType.Hu.value:
            if self.isHu(cardList,cardNum):
                self.resNotifyOpCard(notifyP, opType, cardNum)
            else:
                self.recursiveOutCardNum(self.getNextPlayer(notifyP.playerID),opType,cardNum)
        elif opType == card.OpType.Gang.value:
            if self.isGang(lockList,cardNum):#已碰的牌不能杠
                self.recursiveOutCardNum(self.getNextPlayer(notifyP.playerID), opType, cardNum)
            elif self.isGang(cardList,cardNum):#别人打的牌只能 明杠
                self.resNotifyOpCard(notifyP, opType, cardNum)
            else:
                self.recursiveOutCardNum(self.getNextPlayer(notifyP.playerID),opType,cardNum)
        elif opType == card.OpType.Peng.value:
            if self.isPeng(cardList,cardNum):
                self.resNotifyOpCard(notifyP, opType, cardNum)
            else:
                self.recursiveOutCardNum(self.getNextPlayer(notifyP.playerID),opType,cardNum)

    # 前端碰牌请求
    def opPengReq(self,data):
        p = self.getPlayerByPlayerID(data['playerID'])
        if not self.isPeng(p.cardList, self.curOutCardNum):
            print '炸碰'
            print p.cardList
            print p.lockList
            print self.curOutCardNum
            return
        self.curPlayer.cardList.append(self.curOutCardNum)
        for i in range(0,3):
            self.curPlayer.lockList.append(self.curOutCardNum)
        self.resOpResult(self.curPlayer, card.OpType.Peng.value, self.curOutCardNum)
        self.resNotifyOpRes(card.OpType.Peng.value,self.curOutCardNum)

    # 前端杠牌请求
    def opGangReq(self,data):
        p = self.getPlayerByPlayerID(data['playerID'])
        if False==self.isGang(p.cardList, self.curOutCardNum) and False==self.isGang(p.lockList, self.curOutCardNum):
            print '炸杠'
            print p.cardList
            print p.lockList
            print self.curOutCardNum
            return
        if True==self.isGang(p.lockList, self.curOutCardNum):
            print ('明杠')
            p.rate['mg'] = (p.rate['mg'] > 0 and p.rate['mg'] or 1)
            p.rate['mg'] *= self.mgRate
        else:
            print '暗杠'
            p.rate['ag'] = (p.rate['ag'] > 0 and p.rate['ag'] or 1)
            p.rate['ag'] *= self.agRate
        for i in range(0,4):
            self.curPlayer.lockList.append(self.curOutCardNum)
        self.curPlayer.cardList.append(self.curOutCardNum)
        self.resOpResult(self.curPlayer, card.OpType.Gang.value, self.curOutCardNum)#返回杠牌结果
        self.resNotifyOpRes(card.OpType.Gang.value, self.curOutCardNum)#通知所有牌桌玩家杠牌结果
        self.opSendCard()#给杠牌玩家再发一张牌
        print self.curPlayer.cardList
    # 前端胡牌请求
    def opHuReq(self,data):
        p = self.getPlayerByPlayerID(data['playerID'])
        hu1 = False
        hu2 = False
        if self.isHu(p.cardList,self.curOutCardNum):#平胡
            hu1 = True
        if False == hu1:
            tempList = p.cardList[:]
            tempList.remove(self.curOutCardNum)
            if self.isHu(tempList,self.curOutCardNum):#自摸
                hu2 = True
                self.curPlayer.rate['zm'] = self.zmRate

        if False==hu1 and False==hu2:
            print '炸胡'
            print p.cardList
            print p.lockList
            print self.curOutCardNum
            return

        if len(self.curPlayer.lockList) > 0 :
            self.curPlayer.rate['mq'] = self.mqRate
        if True==hu1:
            self.curPlayer.cardList.append(self.curOutCardNum)
        self.resOpResult(self.curPlayer, card.OpType.Hu.value, self.curOutCardNum)
        self.resNotifyOpRes(card.OpType.Hu.value, self.curOutCardNum)
        self.end(self.curPlayer)

    #请求
    # 玩家请求操作牌
    def reqControlCard(self, data):
        opType = data['opType']
        playerID = data['playerID']
        if opType == card.OpType.Chu.value:
            if playerID != self.curPlayer.playerID:
                print '出牌失败，不是当前操作玩家'
                print ('your id = %d,current op id = %d'(playerID, self.curPlayer.playerID))
                return
        else:
            if playerID != self.curNotifyPlayer.playerID:
                print '操作牌失败，不是当前操作玩家'
                print ('your id = %d,current op id = %d'(playerID, self.curPlayer.playerID))
                return
            elif opType != card.OpType.Guo.value:
                self.curPlayer = self.curNotifyPlayer

        if (opType == card.OpType.Peng.value):
            # 碰牌
            self.opPengReq(data)
        elif (opType == card.OpType.Gang.value):
            # 杠牌
            self.opGangReq(data)
        elif (opType == card.OpType.Ting.value):
            # 听牌
            pass
        elif (opType == card.OpType.Hu.value):
            # 胡牌
            self.opHuReq(data)
        elif (opType == card.OpType.Chu.value):
            # 出牌
            self.opChuReq(data)
        elif (opType == card.OpType.Guo.value):
            # 过牌
            self.recursiveOutCardNum(self.getNextPlayer(playerID),self.curNotifyOpType,self.curOutCardNum)
        else:
            print '未知的操作请求'

    #回复操作牌的结果
    def resOpResult(self,p,opType,cardNum):
        msg = {}
        msg['id'] = protocal.operate
        msg['opType'] = opType
        msg['cardNum'] = cardNum
        msg['cardList'] = p.cardList
        msg['lockList'] = p.lockList
        d = json.dumps(msg)
        jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID, d)
    #通知操作牌
    def resNotifyOpCard(self,p,opType,cardNum):
        self.curNotifyPlayer = p
        msg = {}
        msg['id'] = protocal.notifyOperate
        msg['opType'] = opType
        msg['cardNum'] = cardNum
        msg['cardList'] = p.cardList
        msg['lockList'] = p.lockList
        msg['playerID'] = p.playerID
        d = json.dumps(msg)
        print '通知操作牌'
        print (d)
        for p in self.table.posDict.values():
            jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID, d)
        self.addDelayCall(11, self.opTimeOut)
        self.notifyUpdateData()

    #通知操作牌 结果
    def resNotifyOpRes(self,opType,cardNum):
        for p in self.table.posDict.values():
            msg = {}
            msg['id'] = protocal.notifyOperateRes
            msg['opType'] = opType
            msg['playerID'] = p.playerID
            msg['cardNum'] = cardNum
            d = json.dumps(msg)
            jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID, d)
    #通知结算
    def resNotifyResult(self):
        msg = {}
        msg['id'] = protocal.notifyResult
        players= {}
        for p in self.table.posDict.values():
            player = {}
            if p.playerID == self.curPlayer.playerID:
                isWin = True
                player['cardList'] = self.curPlayer.cardList
                player['lockList'] = self.curPlayer.lockList
                player['rate'] = p.rate
            else:
                isWin = False
            player['isWin'] = isWin
            player['playerID'] = p.playerID
            players[p.pos] = player
        msg['players'] = players
        d = json.dumps(msg)
        for p in self.table.posDict.values():
            jjGlobal.gameserver.gameServerHand.sendMsg(p.playerID, d)

    #是否相应客户端发过来的消息
    def isRecMsg(self,data):
        playerID = data['playerID']
        for p in self.table.posDict.values():
            if p.playerID == playerID:
                return True
        return False


    def getNextPlayer(self,playerID):
        pos = self.getPosByPlayerID(playerID)
        for i in range(1,6):
            tryPos = (pos + i)%6
            if self.table.posDict.has_key(tryPos):
                p = self.table.posDict[tryPos]
                if p.isPlaying == True:
                    return p
        print '获取下一个位置玩家失败2'
    def getPosByPlayerID(self,playerID):
        pos = None  # 0 1 2 3 4 5
        for key, p in self.table.posDict.items():
            if playerID == p.playerID:
                pos = key
                break
        if pos == None:
            print '获取玩家位置失败'
            print playerID
        return pos
    def getPlayerByPlayerID(self,playerID):
        for p in self.table.posDict.values():
            if(p.playerID == playerID):
                return p
        return None

    def isHu(self,cdList,cardNum):
        cardList = cdList[:]
        cardList.append(cardNum)
        if cardList == None or type(cardList) != list or len(cardList) < 8:
            print '给定如下的牌不能判断是否胡牌'
            print cardList
            return False
        cardList.sort()
        self.del4SameCard(cardList)
        self.del3SameCard(cardList)
        self.del3Shunzi(cardList)
        if 0==len(cardList):
            return True
        else:
            return False
    def isGang(self,cdList,cardNum):
        cardList = cdList[:]
        cardList.append(cardNum)
        if cardList == None or type(cardList) != list or len(cardList) < 4:
            return False
        if cardList.count(cardNum) == 4:
            return True
        return False

    def isPeng(self,cdList, cardNum):
        cardList = cdList[:]
        cardList.append(cardNum)
        if cardList == None or type(cardList) != list or len(cardList) < 8:
            print '给定如下的牌不能判断是否碰牌'
            print cardList
            return False
        if cardList.count(cardNum) == 3:
            return True
        return False
    def del4SameCard(self,cardList):# 去掉四张一样的牌
        for i in cardList:
            if 4 == cardList.count(i):
                for j in range(0, 4):
                    cardList.remove(i)
                self.del4SameCard(cardList)
                break

    def del3SameCard(self,cardList):  # 去掉三张一样的牌
        for i in cardList:
            if 3 == cardList.count(i):
                for j in range(0, 3):
                    cardList.remove(i)
                self.del3SameCard(cardList)
                break

    def del3Shunzi(self,cardList):  # 去掉三个顺子
        length = len(cardList)
        if length < 3:
            return
        first = cardList[0]
        if ((first + 1) in cardList) and ((first + 2) in cardList):
            cardList.remove(first)
            cardList.remove(first + 1)
            cardList.remove(first + 2)
            self.del3Shunzi(cardList)
    def clearOfflinePlayer(self):
        for i in range(0,6):
            if self.table.posDict.has_key(i):
                p = self.table.posDict[i]
                if not jjGlobal.gameserver.gameServerHand.isPlayerOnline(p.playerID):
                    self.table.leavePlayer(p.playerID)